#include <stdio.h>
//自定义类型：联合和枚举
//1️⃣联合体(共用体) ———— union
//①联合体类型的声明:
union  Un  
{
    char c;
    int i; 
};
union    //匿名联合体
{
    char c;
    double b;
}u;//匿名联合体类型的变量
//②联合体的特点：
//例题：
union  Un  //联合类型的声明
{
    char c;//1
    int i; //4
};
struct S
{
    char c;//1  8  1
    int i; //4  8  4
};
int main()
{
    union Un u={0};//联合变量的定义
    struct S s;
    printf("%zu\n",sizeof(union Un));//4  
    printf("%zu\n",sizeof(struct S));//8
    printf("%p\n",&u);    //000000000061FE5C
    printf("%p\n",&(u.c));//000000000061FE5C
    printf("%p\n",&(u.i));//000000000061FE5C
    //说明所有成员共用同一块内存空间

    u.i=0x11223344;
    u.c=0x55;
    printf("%x",u.i);//11223355 （小端）
    //给联合体其中一个成员赋值，其他成员的值也跟着变化
    return 0;
}
//③联合体大小的计算：
union Un1
{
    char c[5];//1  8  1   数组每个元素是char类型，所以成员变量大小是1
    int i;    //4  8  4
};//最大对齐数是4，最大成员大小是5，因为要对齐到最大对齐数的整数倍，所以联合体的大小是8
union Un2
{
    short c[7];//2  8  2
    int i;     //4  8  4
};//最大对齐数是4，最大成员大小是14，因为要对齐到最大对齐数的整数倍，所以联合体的大小是16
int main()
{
    printf("%zu\n", sizeof(union Un1));//8
    printf("%zu\n", sizeof(union Un2));//16
    return 0;
}
//⚠️礼品兑换单例题：
//我们要搞一个活动，要上线一个礼品兑换单，礼品兑换单中有三种商品：图书、杯子、衬衫。
//每一种商品都有：库存量、价格、商品类型和商品类型相关的其他信息。
//其他信息如下：
//图书：书名、作者、页数
//杯子：设计
//衬衫：设计、可选颜色、可选尺寸
//第一种设计：
struct gift_list
{
    //公共属性
    int stock_number;//库存量
    double price; //定价
    int item_type;//商品类型
    //特殊属性
    char title[20];//书名
    char author[20];//作者
    int num_pages;//页数
    char design[30];//设计
    int colors;//颜色
    int sizes;//尺寸
};
//但是这种结构的设计中包含了所有礼品的各种属性，这样使得结构体的大小就会偏大，比较浪费内存。因为对于礼品兑换单中的商品来说，只有部分属性信息是常用的。
//比如：商品是图书，就不需要design、colors、sizes。
//所以我们就可以把公共属性单独写出来，剩余属于各种商品本身的属性使用联合体，这样就可以减少所需的内存空间，一定程度上节省了内存。
//第二种设计：（优化） P117
struct gift_list
{
    int stock_number;//库存量
    double price; //定价
    int item_type;//商品类型
    union 
    {
        struct
        {
            char title[20];//书名
            char author[20];//作者
            int num_pages;//页数
        }book;
        struct
        {
            char design[30];//设计
        }mug;
        struct
        {
            char design[30];//设计
            int colors;//颜色
            int sizes;//尺存
        }shirt;
    }item;
};
int main()
{
    printf("%zu\n",sizeof(struct gift_list));//64
    return 0;
}
//④联合体的应用：
//判断当前机器是大端还是小端。  vscode 37.c
int check_sys()
{
    union   //匿名联合体
    {
        int i;
        char c;
    }un;
    un.i = 1;
    return un.c;//返回1是小端，返回0是大端 
}
int main()
{
    if(check_sys())
    printf("小端\n");//输出：小端
    else
    printf("大端\n");
    return 0;
}
//2️⃣枚举   见vscode 3.c
//①枚举类型的声明：
enum weekday //星期
{
    mon,tue,wed,thu,fri,sat,sun
};
enum sex     //性别
{
    male,female,secret
};
enum Color   //三原色
{
    RED,GREEN,BLUE
};
//{}中的内容是枚举类型的可能取值，也叫枚举常量。枚举常量没有开辟空间，只是一些值。
//②枚举常量的值：
//默认从0开始，依次递增1，当然在声明枚举类型的时候也可以赋初值。
enum A
{
    //这里可以给枚举常量赋初始值
    x,y=9,z,p,q=y+4
//  0  9 10 11  13
};
int main()
{
    printf("%d\n",x);//输出：0
    //x=20;  这里不能改变常量的值，所以是错的
    return 0;
}
//③枚举类型的使用：
//模拟计算机 vscode 8.2.c 8.3.c
enum Option
{
    exit,add,sub,mul,div
//    0   1   2   3   4
};
void menu()
{
    printf("1.add    2.sub\n");
    printf("3.mul    4.div\n");
    printf("    0.exit\n    ");
}
int main()
{
    int input;
    int x,y;
    do
    {
        menu();
        printf("请选择：");
        scanf("%d",&input);
        switch(input)
        {
            case add: //这里类型不一样，intup是int类型，add等是enum Option类型，但是C++这里也不会报错，因为枚举常量本质是整型，而这里又不是直接赋值。
            //……
            break;
            case sub:
            //……
            break;
            case mul:
            //……
            break;
            case div:
            //……
            break;
            case exit:
            printf("退出计算器\n");
            break;
            default:
            printf("选择错误，请重新选择\n");
            break;
        }
    } while(input);
    return 0;
}

enum Color
{
    RED=1,
    GREEN=2,
    BLUE=4
};
int main()
{
    enum Color clr = GREEN;//使用枚举常量给枚举变量赋值
    enum Color c = 8;//不报错
    //拿整数给枚举变量赋值，在.c文件中不报错，在.cpp文件中报错
    //拿整数给枚举变量赋值，在C语言中是可以的，但是在C++是不行的
    //C++的类型检查比较严格，只能拿枚举类型里的取值（枚举常量）去赋值枚举变量，或者强制类型转换后赋值。
    return 0;
}

//C++中（.cpp文件中）
//switch语句主要关注表达式和case后常量表达式的类型兼容，C++中普通枚举能用于switch是因为其底层整型性质满足类型要求；而直接赋值更侧重于枚举变量只能接受枚举类型内定义的常量值，对赋值来源类型检查严格。
//在switch中，是用枚举变量作为判断表达式，case后用枚举常量分支处理；直接赋值是将值赋予枚举变量，规则上限制只能是枚举常量（或经合适转换的数值 ）。